/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University. All rights reserved.
 * 
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license; 2) All redistributions in binary form
 * must reproduce the above copyright notice, this list of conditions and the
 * disclaimer listed in this license in the documentation and/or other materials
 * provided with the distribution; 3) Any documentation included with all
 * redistributions must include the following acknowledgement:
 * 
 * "This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 * 
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 * 
 * 4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University. For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5) Products
 * derived from this software may not be called NaradaBrokering, nor may Indiana
 * University or Community Grids Lab or NaradaBrokering appear in their name,
 * without prior written permission of ARTI.
 * 
 * 
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of any
 * other entity. Indiana University disclaims any liability to any recipient for
 * claims brought by any other entity based on infringement of intellectual
 * property rights or otherwise.
 * 
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.service.qos.impl;

import java.util.Enumeration;
import java.util.Hashtable;

import cgl.narada.event.EventHeaders;
import cgl.narada.event.EventID;
import cgl.narada.event.NBEvent;
import cgl.narada.service.qos.ConsumerConstraints;
import cgl.narada.service.qos.ProducerConstraints;
import cgl.narada.service.qos.QosEvent;
import cgl.narada.service.qos.QosIdentifiers;

/**
 * An implementation of the the Qos Event.
 * 
 * @author Shrideep Pallickara $Date$ $Revision$
 */

public class QosEventImpl implements QosEvent {

    private NBEvent nbEvent;
    private boolean hasProducerConstraints = false;
    private ProducerConstraints producerConstraints = null;

    private boolean hasConsumerConstraints = false;
    private ConsumerConstraints consumerConstraints = null;

    private int numOfConsumerConstraints = 0;
    private boolean hasMultipleConsumerConstraints = false;

    private Hashtable consumerConstraintsTable;

    private QosIdentifiers qosIdentifiers;

    private EventID eventId;

    public QosEventImpl(NBEvent nbEvent) {
        this.nbEvent = nbEvent;
        qosIdentifiers = new QosIdentifiersImpl();
        initiailizeEventId();
    }

    public QosEventImpl(NBEvent nbEvent, ProducerConstraints producerConstraints) {
        this.nbEvent = nbEvent;
        this.producerConstraints = producerConstraints;
        hasProducerConstraints = true;
        qosIdentifiers = new QosIdentifiersImpl();
        initiailizeEventId();
    }

    private void initiailizeEventId() {
        eventId = null;
        if (nbEvent.hasEventHeaders()) {
            EventHeaders headers = nbEvent.getEventHeaders();
            if (headers.hasEventId()) {
                eventId = headers.getEventId();
            }/* end if has eventID */
        }/* end if has headers */
    }

    /** Returns the event id associated with the encapsulated event */
    public EventID getEventId() {
        return eventId;
    }

    /** Returns the encapsulated event */
    public NBEvent getNBEvent() {
        return nbEvent;
    }

    /** Indicates if there are any producer constraints that were specified */
    public boolean hasProducerConstraints() {
        return hasProducerConstraints;
    }

    /** Return the producer constraints */
    public ProducerConstraints getProducerConstraints() {
        return producerConstraints;
    }

    /** Indicates the current processing state of this event */
    public QosIdentifiers getQosIdentifiers() {
        return qosIdentifiers;
    }

    /** Indicates if there are any consumer constraints that were specified */
    public boolean hasConsumerConstraints() {
        return hasConsumerConstraints;
    }

    /** Returns the number of pending consumer constraints */
    public int getNumberofConsumerConstraints() {
        return numOfConsumerConstraints;
    }

    /**
     * Indicates if the event had multiple consumer constraints that were
     * specified
     */
    public boolean hasMultipleConsumerConstraints() {
        return hasMultipleConsumerConstraints;
    }

    /**
     * Add consumer constraints associated with this event. This method can be
     * called repeatedly to add more than 1 registered consumer constraints from
     * matching consumers.
     */
    public void addConsumerConstraints(ConsumerConstraints _consumerConstraints) {
        /**
         * No need to populate the consumerConstraintsTable if there is only one
         * consumer constraint
         */
        if (numOfConsumerConstraints == 0) {
            this.consumerConstraints = _consumerConstraints;
            hasConsumerConstraints = true;
            numOfConsumerConstraints++;
            return;
        }

        if (numOfConsumerConstraints == 1) {
            addToConsumerConstraintsTable(consumerConstraints);
            addToConsumerConstraintsTable(_consumerConstraints);
            numOfConsumerConstraints++;
            hasMultipleConsumerConstraints = true;
        }

        addToConsumerConstraintsTable(_consumerConstraints);
        numOfConsumerConstraints++;
    }

    /** Add to the consumer constraints table */
    private void addToConsumerConstraintsTable(
            ConsumerConstraints consumerConstraints) {

        if (numOfConsumerConstraints == 1) {
            consumerConstraintsTable = new Hashtable();
        }

        int keyId = ((ConsumerConstraintsImpl) consumerConstraints)
                .getConstraintIdentifier();
        Object key = new Integer(keyId);

        consumerConstraintsTable.put(key, consumerConstraints);
    }

    public boolean hasSatisfiedAllConstraints() {
        if (hasConsumerConstraints) {
            if (numOfConsumerConstraints == 0) {
                return true;
            }
        }

        if (hasProducerConstraints) {
            boolean satisfied = producerConstraints
                    .hasSatisfiedSpecifiedConstraints(qosIdentifiers);
            return satisfied;
        }

        return false;
    }

    /**
     * If there are any consumer constraints that have been satisfied, based on
     * the QosIdentifiers associated with this event -- that constraint will be
     * returned.
     * 
     * <br>
     * The method will return a NULL under the following conditions (a) If no
     * consumer constraints have been satisfied OR (b) If all constraints have
     * been satisfied OR (c) If no consumer constraints were specified
     */
    public ConsumerConstraints getSatisfiedConsumerConstraints() {
        boolean satisfied = false;

        /** If no consumer constraints were specified return null */
        if (!hasConsumerConstraints) {
            return null;
        }

        /** If all constraints have been satisfied return null */
        if (numOfConsumerConstraints == 0) {
            return null;
        }

        /**
         * If there was a total of one consumer constraint, as will most often
         * be the case, this ensures that operations are executed faster
         */
        if (hasConsumerConstraints) {
            if (!hasMultipleConsumerConstraints) {
                satisfied = consumerConstraints
                        .hasSatisfiedSpecifiedConstraints(qosIdentifiers);
                if (satisfied) {
                    numOfConsumerConstraints--;
                    return consumerConstraints;
                }

                return null;
            }
        }

        /**
         * This part on deals with the case where there were multiple consumer
         * constraints that were specified and were associated with this event
         */
        Enumeration keys = consumerConstraintsTable.keys();
        while (keys.hasMoreElements()) {
            Object _key = keys.nextElement();
            ConsumerConstraints _constraints = (ConsumerConstraints) consumerConstraintsTable
                    .get(_key);
            satisfied = _constraints
                    .hasSatisfiedSpecifiedConstraints(qosIdentifiers);
            if (satisfied) {
                numOfConsumerConstraints--;
                consumerConstraintsTable.remove(_key);
                return _constraints;
            }
        } /* end while */

        return null;
    }

    /**
     * This method returns the service which the event must be routed to for
     * further processing. If this returns 0, no further processing is required.
     * A 0 indicates that no service was found for special processing, and that
     * the event is ready to be released (either to the application or to the
     * communications stream )
     */
    public int getServiceToBeRoutedTo() {
        return 0;
    }

    /** Return pending consumer constraints */
    public ConsumerConstraints[] getPendingConsumerConstraints() {
        ConsumerConstraints[] pendingStuff = null;

        /** If all constraints have been satisfied return null */
        if (numOfConsumerConstraints == 0) {
            return null;
        }

        if (!hasConsumerConstraints) {
            return null;
        }

        /* Fixing the qosevent problem */
        // ConsumerConstraints _constraints = getSatisfiedConsumerConstraints();
        if (hasMultipleConsumerConstraints) {
            /** Clear up those constraints that have been satisfied */
            /* Fixing the qosevent problem */
            // while (_constraints != null) {
            // _constraints = getSatisfiedConsumerConstraints();
            // }
            if (consumerConstraintsTable.size() == 0) {
                return null;
            } else {
                pendingStuff = (ConsumerConstraints[]) consumerConstraintsTable
                        .values().toArray(new ConsumerConstraints[0]);
            } /* end consumerConstraintsTable.size() == 0 */

        } else {
            pendingStuff = new ConsumerConstraints[1];
            pendingStuff[0] = consumerConstraints;
        }
        /** if (hasMultipleConsumerConstraints) */

        return pendingStuff;
    }

}
